package com.cherrypt.api.core.controller.tracker;

import com.cherrypt.api.common.constant.WebConst;
import com.cherrypt.api.common.enums.LogEnum;
import com.cherrypt.api.common.enums.ResultEnum;
import com.cherrypt.api.common.enums.TrackerResponseEnum;
import com.cherrypt.api.common.enums.UserInfoEnum;
import com.cherrypt.api.common.exception.TipException;
import com.cherrypt.api.common.util.RequestUtils;
import com.cherrypt.api.common.util.TrackerResponseUtils;
import com.cherrypt.api.core.service.*;
import com.cherrypt.api.core.util.CheckRuleUtils;
import com.cherrypt.api.core.util.IPUtils;
import com.cherrypt.api.core.util.LogUtils;
import com.cherrypt.api.model.dataobject.Discount;
import com.cherrypt.api.model.dataobject.Peer;
import com.cherrypt.api.model.dataobject.Torrent;
import com.cherrypt.api.model.dataobject.UserInfo;
import com.cherrypt.api.model.vo.IPEndpointVo;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static com.cherrypt.api.common.constant.TrackerConst.*;

/**
 * @author intent
 * @date 2019/7/24 10:56
 * @about <link href='http://zzyitj.xyz/'/>
 */
@RestController
@RequestMapping(value = "${api.url.tracker.value}")
public class AnnounceController {
    private static final Logger logger = LoggerFactory.getLogger(AnnounceController.class);

    @Autowired
    private PeerService peerService;

    @Autowired
    private TorrentService torrentService;

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private LogService logService;

    @Autowired
    private DiscountService discountService;

    @Value(value = "${api.config.tracker.uploadOverSpeed}")
    private long uploadOverSpeed;

    @GetMapping(value = "${api.url.tracker.scrape}")
    public String scrape(HttpServletRequest request) throws DecoderException {
        // 取得hash，这里只处理一个hash的情况
        String infoHash = RequestUtils.getQueryString(request.getQueryString(), INFO_HASH_PARAM);
        TrackerResponseEnum trackerResponseEnum = CheckRuleUtils.checkHash(infoHash);
        if (trackerResponseEnum == TrackerResponseEnum.SUCCESS) {
            Torrent torrent = torrentService.getTorrentByHash(URLCodec.decodeUrl(infoHash.getBytes()));
            // 检查hash在种子库中是否存在
            if (torrent != null) {
                return TrackerResponseUtils.success();
            }
            return TrackerResponseUtils.error(TrackerResponseEnum.NO_SEED);
        }
        return TrackerResponseUtils.error(trackerResponseEnum);
    }

    /**
     * Tracker Server逻辑
     * 1. 检查参数，错误返回，正确执行第2步
     * 2. 判断event是否等于started，不等于执行第4步，等于执行第3步
     * 3. 判断hash在种子库中是否存在，不存在先判断left是否等于0，等于0说明是做种，然后构造peer加入peers，存在就先加入peers再返回
     * 4. 判断hash在种子库中是否存在，存在执行第5步，不存在返回错误
     * 5. 判断event是否等于complete，判断peers是否为null，为null返回错误，否则构造peer加入peers
     * 6. 判断event是否等于stopped，从peers中移除peer
     * 7. 判断event是否为空，判断peers是否为null，为null返回错误，否则返回peer
     *
     * @param request 请求头
     * @return tracker info
     */
    @GetMapping(value = "${api.url.tracker.announce}")
    public String announce(HttpServletRequest request) throws DecoderException, TipException {
        // 取得hash，这里只处理一个hash的情况
        String infoHash = RequestUtils.getQueryString(request.getQueryString(), INFO_HASH_PARAM);
        TrackerResponseEnum trackerResponseEnum = CheckRuleUtils.checkRule(request, infoHash);
        if (trackerResponseEnum == TrackerResponseEnum.SUCCESS) {
            String event = request.getParameter(EVENT_PARAM);
            // 判断是不是endpoint
            long port = Long.valueOf(request.getParameter(PORT_PARAM));
            String ipv6 = request.getParameter(IPV6_PARAM);
            IPEndpointVo ipEndpoint = RequestUtils.getIPEndpoint(ipv6);
            if (ipEndpoint != null) {
                ipv6 = ipEndpoint.getHost();
                port = ipEndpoint.getPort();
            }
            // 判断种子库中是否存在该种子
            Torrent torrent = torrentService.getTorrentByHash(URLCodec.decodeUrl(infoHash.getBytes()));
            if (torrent == null) {
                return TrackerResponseUtils.error(TrackerResponseEnum.NO_SEED);
            } else {
                String passkey = request.getParameter(PASSKEY_PARAM);
                UserInfo userInfo = userInfoService.getUserInfoByPasskey(passkey);
                if (userInfo == null) {
                    return TrackerResponseUtils.error(TrackerResponseEnum.PASSKEY_ERROR);
                }
                if (userInfo.getStatus() == UserInfoEnum.BAN.getCode()) {
                    return TrackerResponseUtils.error(TrackerResponseEnum.ACCOUNT_BAN);
                }
                // 构造peer
                Peer peer = new Peer();
                peer.setUid(userInfo.getUid());
                peer.setUsername(userInfo.getUsername());
                peer.setIp(IPUtils.getIpAddr(request));
                if (StringUtils.isNotBlank(ipv6)) {
                    peer.setIpv6(ipv6);
                }
                peer.setPort(port);
                peer.setPeerId(request.getParameter(PEER_ID_PARAM));
                peer.setLeft(Long.parseLong(request.getParameter(LEFT_PARAM)));
                peer.setUploaded(Long.valueOf(request.getParameter(UPLOAD_PARAM)));
                peer.setDownloaded(Long.valueOf(request.getParameter(DOWNLOAD_PARAM)));
                peer.setLastConnectTime(System.currentTimeMillis());
                // 判断peersMap是否已经存在tid的peers
                List<Peer> peers = peerService.getAllPeer(torrent.getId());
                // 判断event=started
                if (STARTED.equals(event)) {
                    // 判断left是否等于0，等于0说明是做种
                    if (peer.getLeft() == 0) {
                        // 添加到peers
                        peerService.savePeer(torrent.getId(), peer);
                        // completing + 1
                        peerService.completeIncrOne(torrent.getId());
                        return TrackerResponseUtils.success();
                    } else {
                        // redis中没有该种子
                        if (peers == null || peers.size() == 0) {
                            return TrackerResponseUtils.error(TrackerResponseEnum.NO_PEER);
                        } else {
                            // downloading +1
                            peerService.saveDownloadPeer(torrent.getId(), peer);
                            return TrackerResponseUtils.success(peers);
                        }
                    }
                } else if (peers != null) {
                    if (COMPLETED.equals(event)) {
                        // completing +1
                        // 存储到peers
                        // downloading -1
                        peerService.completeIncrOne(torrent.getId());
                        // 获取在数据库中的downloadPeer
                        Peer downloadPeer = peerService.getDownloadPeer(torrent.getId(), peer);
                        if (downloadPeer != null) {
                            updateData(peer, downloadPeer, torrent, request);
                            peerService.removeDownloadPeer(torrent.getId(), peer);
                        }
                        peerService.savePeer(torrent.getId(), peer);
                        return TrackerResponseUtils.success(peers);
                    } else if (STOP.equals(event) || PAUSED.equals(event)) {
                        // 获取在数据库中的downloadPeer
                        Peer downloadPeer = peerService.getDownloadPeer(torrent.getId(), peer);
                        if (downloadPeer != null) {
                            updateData(peer, downloadPeer, torrent, request);
                            peerService.removeDownloadPeer(torrent.getId(), peer);
                        } else {
                            peerService.removePeer(torrent.getId(), peer);
                        }
                        return TrackerResponseUtils.success();
                    } else if (StringUtils.isBlank(event)) {
                        Peer oldPeer = peerService.getPeer(torrent.getId(), peer);
                        if (oldPeer != null) {
                            updateData(peer, oldPeer, torrent, request);
//                            peerService.updatePeerTTL(torrent.getId(), oldPeer);
                            peerService.savePeer(torrent.getId(), peer);
                        } else {
                            oldPeer = peerService.getDownloadPeer(torrent.getId(), peer);
                            if (oldPeer != null) {
                                updateData(peer, oldPeer, torrent, request);
//                                peerService.updateDownloadPeerTTL(torrent.getId(), oldPeer);
                                peerService.saveDownloadPeer(torrent.getId(), peer);
                            }
                        }
                        return TrackerResponseUtils.success(peers);
                    }
                } else {
                    return TrackerResponseUtils.error(TrackerResponseEnum.NO_PEER);
                }
            }
        }
        return TrackerResponseUtils.error(trackerResponseEnum);
    }

    private boolean updateData(Peer peer, Peer oldPeer, Torrent torrent, HttpServletRequest request)
            throws TipException {
        Discount discount = discountService.getDiscount(torrent.getDiscountId());
        // 当前时间戳 - 上一次连接时间戳
        long currentMillis = System.currentTimeMillis() - oldPeer.getLastConnectTime();
        // 计算连接时间 = 之前连接时间戳 + （当前时间戳 - 上一次连接时间戳）
        peer.setConnectTime(oldPeer.getConnectTime() + currentMillis);
        // 计算本次下载和上传量
        long uploadSpeed = peer.getUploaded() - oldPeer.getUploaded();
        long downloadSpeed = peer.getDownloaded() - oldPeer.getDownloaded();
        // 计算超速
        if (uploadSpeed / (currentMillis / WebConst.SECOND) > uploadOverSpeed) {
            userInfoService.updateStatusByUid(peer.getUid(), UserInfoEnum.BAN);
            LogUtils.writeLog(logService, peer.getUid(), LogEnum.UPLOAD_OVER_SPEED, peer.toString(), request);
            throw new TipException(ResultEnum.ERROR);
        }
        // 优惠后的上传量和下载量为最终实际上传下载量
        long upload = (long) (uploadSpeed * discount.getUpload());
        long download = (long) (downloadSpeed * discount.getDownload());
        peer.setUploadSpeed(upload);
        peer.setDownloadSpeed(download);
        // 上传和下载都是0就不必更新用户上传量和下载量
        if (upload == 0 && download == 0) {
            return false;
        }
        // 更新用户上传和下载
        return userInfoService.updateUDByUid(peer.getUid(), uploadSpeed, downloadSpeed);
    }
}
